<?php


namespace App\Helpers\Redis;

use Illuminate\Support\Facades\Redis;

/**
 * Redis助手类
 *
 * @tips    注意此助手类是按 phpRedis 扩展里面的使用习惯封装的
 *
 * Class RedisHelper
 *
 * @package App\Helpers\Redis
 * @author  kingsg
 * @method static incr(string $key)                // 整数原子加1
 * @method static incrBy(string $key, int $num)    // 原子加指定的整数
 * @method static decr(string $key)                // 整数原子减1
 * @method static decrBy(string $key, int $num)    // 原子减指定的整数
 * @method static EXPIRE(string $key, int $expired)
 * @method static hSet(string $key, string $field, mixed $value)
 * @method static hGet(string $key, string $field)
 * @method static hDel(mixed|string $hashKey, int|string|array $field)
 * @method static lLen(string $key)
 * @method static rPop(string $redisKey)
 */
class RedisHelper extends BaseRedisHelper
{
    /**
     * keys规则：key类型_业务描述
     * 例：
     *    STRING_DEMO1
     *    LIST_DEMO1
     *    HASH_DEMO1
     *    SET_DEMO1
     *    ZSET_DEMO1
     *  ......
     */

    const LIST_UNIQUE_ID = 'list_unique_id'; // 服务全局唯一ID队列

    const STRING_LOGIN_CAPTCHA = 'string_login_captcha_%s'; // 登录验证码

    const STRING_JWT_BLACK_LIST = 'string_jwt_black_list_%s'; // jwt黑名单

    /**
     * 加工key
     *
     * @param $key
     * @param ...$value1
     * @return string
     */
    public static function makeKey($key, ...$value1): string
    {
        return sprintf($key, ...$value1);
    }

    /**
     * get
     *
     * @param $key
     * @return mixed
     */
    public static function get($key)
    {
        static::beforeHandler($key);

        $result = Redis::get($key);

        return json_decode($result, true);
    }

    /**
     * set
     * 写入值到Redis，默认有效期是一周
     *
     * @tips  不允许永不过期的key， 如果有特殊需要处理的，请单独传 0
     *
     * Set the string value in argument as value of the key.
     *
     * @since If you're using Redis >= 2.6.12, you can pass extended options as explained in example
     *
     * @param              $key
     * @param string|mixed $value   string if not used serializer
     * @param int|array    $expired [optional] Calling setex() is preferred if you want a timeout.<br>
     *                              Since 2.6.12 it also supports different flags inside an array. Example ['NX', 'EX' => 60]<br>
     *                              - EX seconds -- Set the specified expire time, in seconds.<br>
     *                              - PX milliseconds -- Set the specified expire time, in milliseconds.<br>
     *                              - NX -- Only set the key if it does not already exist.<br>
     *                              - XX -- Only set the key if it already exist.<br>
     *                              <pre>
     *                              // Simple key -> value set
     *                              $redis->set('key', 'value');
     *
     * // Will redirect, and actually make an SETEX call
     * $redis->set('key','value', 10);
     *
     * // Will set the key, if it doesn't exist, with a ttl of 10 seconds
     * $redis->set('key', 'value', ['nx', 'ex' => 10]);
     *
     * // Will set a key, if it does exist, with a ttl of 1000 milliseconds
     * $redis->set('key', 'value', ['xx', 'px' => 1000]);
     * </pre>
     */
    public static function set($key, $value, $expired = REDIS_DEFAULT_TIMEOUT)
    {
        static::beforeHandler($key);

        $value = json_encode($value, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);

        if (is_array($expired)) {
            return Redis::set($key, $value, current($expired), array_key_last($expired), array_pop($expired));
        } else {
            if ($expired) {
                return Redis::set($key, $value, 'EX', $expired);
            } else {
                /**
                 * @tips 一般不要设置不过期的key
                 */
                return Redis::set($key, $value);
            }
        }
    }

    /**
     * lPush 左进
     *
     * @param $key
     * @param $value
     * @return mixed
     */
    public static function push($key, $value)
    {
        static::beforeHandler($key);

        return Redis::lPush($key, json_encode($value));
    }

    /**
     * lPush 左进 批量push
     *
     * @param $key
     * @param ...$value1
     * @return mixed
     */
    public static function mPush($key, ...$value1)
    {
        static::beforeHandler($key);

        foreach ($value1 as $k => &$val) {
            $value1[$k] = json_encode($val);
        }

        return Redis::lPush($key, ...$value1);
    }

    /**
     * rPop 右出
     *
     * @param $key
     * @return mixed|null
     */
    public static function pop($key)
    {
        static::beforeHandler($key);
        $value = Redis::rPop($key);

        if (!is_null($value)) {
            return json_decode($value, true, 512, JSON_BIGINT_AS_STRING);
        }

        return null;
    }

    /**
     * 移除
     *
     * @return mixed
     */
    public static function del($key1, ...$otherKeys)
    {
        static::beforeHandler($key1);
        if ($otherKeys) {
            foreach ($otherKeys as &$key) {
                static::beforeHandler($key);
            }
            array_push($otherKeys, $key1);

            return Redis::del($otherKeys);
        } else {
            return Redis::del($key1);
        }
    }

    /**
     * 调用redis 方法
     *
     * @param $method
     * @param $args
     * @return mixed
     */
    public static function __callStatic($method, $args)
    {
        if (strtolower($method) == 'msetnx') {
            $args0    = $args[0];
            $newArgs0 = [];
            foreach ($args0 as $key => $v) {
                static::beforeHandler($key);
                $newArgs0[$key] = $v;
            }
            $args[0] = $newArgs0;
        } else {
            static::beforeHandler($args[0]);
        }

        return Redis::__callStatic($method, $args);
    }
}
